﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ZeroBugBounce.Missive.Locks;
using System.Threading.Tasks;

namespace ZeroBugBounce.Missive
{
	public partial class Messenger
	{
		EventHandler<HandlerErrorEventArgs> handlerError;
		readonly ILock handlerErrorLock = new NoSpinHybridLock();
		internal bool hasHandleErrorHandlers = false;

		public event EventHandler<HandlerErrorEventArgs> HandlerError
		{
			add
			{
				handlerErrorLock.Enter();
				if (handlerError == null && value != null)
				{
					handlerError = value;
					hasHandleErrorHandlers = true;
				}
				else if(value != null)
				{
					handlerError = (EventHandler<HandlerErrorEventArgs>)Delegate.Combine(handlerError, value);
				}
				handlerErrorLock.Leave();
			}
			remove
			{
				handlerErrorLock.Enter();
				if (handlerError == null)
				{
					return;
				}
				else
				{
					handlerError = (EventHandler<HandlerErrorEventArgs>)Delegate.Remove(handlerError, value);
					if (handlerError == null)
					{
						hasHandleErrorHandlers = false;
					}
				}
				handlerErrorLock.Leave();
			}
		}

		protected internal void RaiseHandleError(Exception exception, Topic topic, object message)
		{
			var errorHandlers = Interlocked.CompareExchange<EventHandler<HandlerErrorEventArgs>>(ref handlerError, null, null);

			if (errorHandlers != null)
			{
				errorHandlers(this, new HandlerErrorEventArgs
				{
					Exception = exception,
					Message = message,
					Topic = topic
				});
			}
		}

		EventHandler<MessageSentEventArgs> messageSent;
		readonly ILock messageSentLock = new NoSpinHybridLock();
		bool hasMessageSentHandlers = false;

		public event EventHandler<MessageSentEventArgs> MessageSent
		{
			add
			{
				messageSentLock.Enter();
				if (messageSent == null && value != null)
				{
					messageSent = value;
					hasMessageSentHandlers = true;
				}
				else
				{
					messageSent = (EventHandler<MessageSentEventArgs>)Delegate.Combine(messageSent, value);
				}
				messageSentLock.Leave();
			}
			remove
			{
				messageSentLock.Enter();
				if (messageSent == null || value == null)
				{
					return;
				}
				else
				{
					messageSent = (EventHandler<MessageSentEventArgs>)Delegate.Remove(messageSent, value);
					if (messageSent == null)
					{
						hasMessageSentHandlers = true;
					}
				}

			}
		}

		protected internal void RaiseMessageSend(Topic topic, object message, bool senderAcceptsReply)
		{
			var messageSentHandlers = Interlocked.CompareExchange<EventHandler<MessageSentEventArgs>>(ref messageSent, null, null);

			if (messageSentHandlers != null)
			{
				var eventArgs = new MessageSentEventArgs
				{
					Topic = topic,
					Message = message,
					SenderAcceptsReply = senderAcceptsReply
				};

				Task.Factory.StartNew(() => messageSentHandlers(this, eventArgs));
			}
		}

		EventHandler<MessageHandledEventArgs> messageHandled;
		readonly ILock messageHandledLock = new NoSpinHybridLock();
		internal bool HasMessageHandledHandlers = false;

		public event EventHandler<MessageHandledEventArgs> MessageHandled
		{
			add
			{
				messageHandledLock.Enter();
				if (messageHandled == null && value != null)
				{
					messageHandled = value;
					HasMessageHandledHandlers = true;
				}
				else
				{
					messageHandled = (EventHandler<MessageHandledEventArgs>)Delegate.Combine(messageHandled, value);
				}
			}
			remove
			{
				messageHandledLock.Enter();
				if (messageHandled == null || value == null)
				{
					return;
				}
				else
				{
					messageHandled = (EventHandler<MessageHandledEventArgs>)Delegate.Remove(messageHandled, value);
					if (messageHandled == null)
					{
						HasMessageHandledHandlers = false;
					}
				}
			}
		}

		protected internal void RaiseMessageHandled(Topic messageTopic, Topic handlerTopic, object message,
			bool senderAcceptsReply, bool handlerCanCancel, bool wasCancelled)
		{
			var messageHandledHandlers = Interlocked.CompareExchange<EventHandler<MessageHandledEventArgs>>(ref messageHandled, null, null);

			if (messageHandled != null)
			{
				var eventArgs = new MessageHandledEventArgs
				{
					MessageTopic = messageTopic,
					HandlerTopic = handlerTopic,
					Message = message,
					SenderAcceptsReply = senderAcceptsReply,
					HandlerCanCancel = handlerCanCancel,
					WasCancelled = wasCancelled
				};

				Task.Factory.StartNew(() => messageHandledHandlers(this, eventArgs));
			}
		}
	}

	public class HandlerErrorEventArgs : EventArgs
	{
		public Exception Exception { get; internal set; }
		public Topic Topic { get; internal set; }
		public object Message { get; internal set; }
	}

	public class MessageSentEventArgs : EventArgs
	{
		public Topic Topic { get; internal set; }
		public object Message { get; internal set; }
		public bool SenderAcceptsReply { get; internal set; }
	}

	public class MessageHandledEventArgs : EventArgs
	{
		public Topic MessageTopic { get; internal set; }
		public Topic HandlerTopic { get; internal set; }
		public object Message { get; internal set; }
		public bool SenderAcceptsReply { get; internal set; }
		public bool HandlerCanCancel { get; internal set; }
		public bool ReplySent { get; internal set; }
		public bool WasCancelled { get; internal set; }
	}
}
